﻿using System;
using System.Collections.Generic;
using System.Text;
using System.Xml;

namespace Weazel.Willows
{
	public class ImplementationConfiguration
	{
		private static ImplementationConfiguration instance;
		public static ImplementationConfiguration Instance
		{
			get { return ImplementationConfiguration.instance; }		
		} 

		public string ImplementationDirectory
		{
			get { return destinationDirectory + "//implementation"; }
		}

		public string HardwareImplementationDirectory
		{
			get { return ImplementationDirectory + "//hardware"; }
		}

		public string IseProjectDirectory
		{
			get { return HardwareImplementationDirectory + "//ise_project"; }
		}

		public string SoftwareImplementationDirectory
		{
			get { return ImplementationDirectory + "//software"; }
		}

		public string SimulationDirectory
		{
			get { return destinationDirectory + "//Simulation"; }
		}

		/// <summary>
		/// Directory to write output files to
		/// </summary>
		private string destinationDirectory;
		public string DestinationDirectory
		{
			get { return destinationDirectory; }
		}

		/// <summary>
		/// List of input xml files
		/// </summary>
		private List<string> inputFiles = new List<string>();
		public List<string> InputFiles
		{
			get { return inputFiles; }
		}

		private Device device;
		public Device Device
		{
			get { return device; }
		}

		/// <summary>
		/// Map mapping partition options to datapath/controller
		/// pairs. Index by datapath name.
		/// </summary>
		private Dictionary<string, PartitionChoice> partition =
			new Dictionary<string, PartitionChoice>();
		public Dictionary<string, PartitionChoice> Partition
		{
			get { return partition; }
		}

		private string systemToConvert;
		public string SystemToConvert
		{
			get { return systemToConvert; }
		}

		private string xilinxProcessorIpLibrary;
		public string XilinxProcessorIpLibrary
		{
			get { return xilinxProcessorIpLibrary; }
		}

		public ImplementationConfiguration()
		{
			instance = this;
		}

		public void Read(ref List<ConfigurationError> errors, XmlElement description)
		{
			System.Diagnostics.Trace.Assert(description.Name == "Configuration");
			System.Diagnostics.Trace.Assert(description["Device"] != null);

			// read destination directory
			if (description["Destination"] == null)
			{
				string message = "Configuration files does not contain a 'Destination' element.";
				errors.Add(new ConfigurationError(message));
			}
			else
			{
				XmlElement destination = description["Destination"];
				destinationDirectory = destination.InnerText.Trim();
			}

			// read name of system to convert
			if (description["SystemToConvert"] == null)
			{
				string message = "Configuration files does not contain a 'SystemToConvert' element.";
				errors.Add(new ConfigurationError(message));
			}
			else
			{
				XmlElement sys = description["SystemToConvert"];
				systemToConvert = sys.InnerText.Trim();
			}

			// edk ip library location
			if (description["XilinxProcessorIpLibrary"] == null)
			{
				string message = "Configuration files does not contain an 'XilinxProcessorIpLibrary' element.";
				errors.Add(new ConfigurationError(message));
			}
			else
			{
				this.xilinxProcessorIpLibrary = description["XilinxProcessorIpLibrary"].InnerText;
			}

			// read input files
			if (description["InputFiles"] == null)
			{
				string message = "Configuration files does not contain an 'InputFiles' element.";
				errors.Add(new ConfigurationError(message));
			}
			else
			{
				XmlElement files = description["InputFiles"];
				foreach (XmlElement file in files.GetElementsByTagName("File"))
				{
					string filename = file.InnerText.Trim();
					if (!System.IO.File.Exists(filename))
					{
						string message =
							String.Format("Input file '{0}' does not exist.", filename);

						errors.Add(new ConfigurationError(message));
					}
					else
					{
						inputFiles.Add(filename);
					}
				}
			}

			// read device properties
			this.device = new Device(description["Device"]);

			if (description["Partition"] == null)
			{
				string message = "Configuration files does not contain a 'Partition' element.";
				errors.Add(new ConfigurationError(message));
			}
			else
			{
				XmlElement partition = description["Partition"];
				foreach (XmlElement partitionChoice in partition.GetElementsByTagName("PartitionChoice"))
				{
					if (!partitionChoice.HasAttribute("name"))
					{
						string message =
							string.Format("Name missing in partition choice.");
						errors.Add(new ConfigurationError(message));
						continue;
					}

					if (!partitionChoice.HasAttribute("target"))
					{
						string message =
							string.Format("Target missing in partition choice.");
						errors.Add(new ConfigurationError(message));
						continue;
					}

					string module = partitionChoice.Attributes["name"].Value.Replace(".", "_");

					if (this.partition.ContainsKey(module))
					{
						string message =
							string.Format("Duplicate partition choice entry for module '{0}'.", module);
						errors.Add(new ConfigurationError(message));
						continue;
					}

					switch (partitionChoice.Attributes["target"].Value)
					{
						case "software":
							{
								PartitionChoice target = PartitionChoice.Software;
								Partition.Add(module, target);
								break;
							}
						case "hardware":
							{
								PartitionChoice target = PartitionChoice.Hardware;
								Partition.Add(module, target);
								break;
							}
						case "parent":
							{
								PartitionChoice target = PartitionChoice.Parent;
								Partition.Add(module, target);
								break;
							}
						default:
							{
								string message =
									string.Format("Invalid partition target '{0}'.", partitionChoice.Attributes["target"].Value);

								errors.Add(new ConfigurationError(message));
								break;
							}
					}
				}
			}
		}
	}
}

